//
// Created by ta&to on 2022/8/25.
//

#include "http_time.h"

static int              cached_gmtoff;
volatile char*         http_current_msec;
volatile http_time_t  *http_cached_time;
volatile http_str_t    http_cached_gmt_time;
volatile http_str_t    http_cached_err_log_time;

static http_time_t        cached_time[NGX_TIME_SLOTS];
static char            cached_hthttp_time[NGX_TIME_SLOTS][sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];
static char            cached_err_log_time[NGX_TIME_SLOTS][sizeof("1970/09/28 12:00:00")];
static char  *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

#define http_timezone(isdst) (- (isdst ? timezone + 3600 : timezone) / 60)

long  http_times()
{
    time_t           sec;
    int      msec;
    struct timeval   tv;
    http_gettimeofday(&tv);
    sec = tv.tv_sec;
    return sec;
}

void http_time_init(void)
{
    http_cached_gmt_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
    http_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;

    http_cached_time = &cached_time[0];
    http_time_update();
}
// 更新时间全局变量
void http_time_update(void)
{
    char          *p0, *p1, *p2, *p3, *p4;
    http_tm_t         tm, gmt;
    time_t           sec;
    int      msec;
    http_time_t      *tp;
    struct timeval   tv;

    if (!http_trylock(&http_time_lock)) {
        return;
    }
    http_gettimeofday(&tv);
    sec = tv.tv_sec;
    msec = tv.tv_usec / 1000;
    http_current_msec = (char*) (sec * 1000 + msec);
    tp = &cached_time[slot];
    if (tp->sec == sec) {
        tp->msec = msec;
        http_unlock(&http_time_lock);
        return;
    }
    if (slot == NGX_TIME_SLOTS - 1) {
        slot = 0;
    } else {
        slot++;
    }
    tp = &cached_time[slot];
    tp->sec = sec;
    tp->msec = msec;
    http_gmtime(sec, &gmt);
    p0 = &cached_hthttp_time[slot][0];
    (void) http_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",
                      week[gmt.http_tm_wday], gmt.http_tm_mday,
                      months[gmt.http_tm_mon - 1], gmt.http_tm_year,
                      gmt.http_tm_hour, gmt.http_tm_min, gmt.http_tm_sec);
    http_localtime(sec, &tm);
    cached_gmtoff = http_timezone(tm.http_tm_isdst);
    tp->gmtoff = cached_gmtoff;

    p1 = &cached_err_log_time[slot][0];
    (void) http_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",
                      tm.http_tm_year, tm.http_tm_mon,
                      tm.http_tm_mday, tm.http_tm_hour,
                      tm.http_tm_min,  tm.http_tm_sec);
    memory_barrier();
    http_cached_time = tp;
    http_cached_gmt_time.data = p0;
    http_cached_err_log_time.data = p1;
    http_unlock(&http_time_lock);
}

// 将时间戳转换成标准时间
void http_gmtime(time_t t, http_tm_t *tp)
{
    int   yday;
    int sec, min, hour, mday, mon, year, wday, days, leap;
    if (t < 0) {
        t = 0;
    }
    days = t / 86400;
    sec = t % 86400;

    if (days > 2932896) {
        days = 2932896;
        sec = 86399;
    }
    wday = (4 + days) % 7;
    hour = sec / 3600;
    sec %= 3600;
    min = sec / 60;
    sec %= 60;
    days = days - (31 + 28) + 719527;
    year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
    yday = days - (365 * year + year / 4 - year / 100 + year / 400);
    if (yday < 0) {
        leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
        yday = 365 + leap + yday;
        year--;
    }
    mon = (yday + 31) * 10 / 306;
    mday = yday - (367 * mon / 12 - 30) + 1;
    if (yday >= 306) {
        year++;
        mon -= 10;
    } else {
        mon += 2;
    }
    tp->http_tm_sec = (int) sec;
    tp->http_tm_min = (int) min;
    tp->http_tm_hour = (int) hour;
    tp->http_tm_mday = (int) mday;
    tp->http_tm_mon = (int) mon;
    tp->http_tm_year = (int) year;
    tp->http_tm_wday = (int) wday;
}

void http_localtime(time_t s, http_tm_t *tm)
{
    http_tm_t  *t;
    t = localtime(&s);
    *tm = *t;
    tm->http_tm_mon++;
    tm->http_tm_year += 1900;
}
// timespec转纳秒
long long tm_to_ns(struct timespec tm)
{
    return tm.tv_sec * 1000000000 + tm.tv_nsec;
}
// 纳秒转timespec
struct timespec ns_to_tm(long long ns)
{
    struct timespec tm;
    tm.tv_sec = ns / 1000000000;
    tm.tv_nsec = ns - (tm.tv_sec * 1000000000);
    return tm;
}

static void _http_time_sleep(long nsec)
{
    struct timespec tv;
    pthread_condattr_t attr_wait;
    pthread_cond_t cond;
    pthread_condattr_init(&attr_wait);
    pthread_condattr_setclock(&attr_wait, CLOCK_REALTIME);
    pthread_cond_init(&cond, &attr_wait);
    pthread_mutex_t mux_wait_lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&mux_wait_lock);
    clock_gettime(CLOCK_REALTIME, &tv);
    long n = tv.tv_nsec + nsec;
    tv.tv_sec += n/1000000000;
    tv.tv_nsec = n%1000000000; // 为节约cpu资源这里设置等待1秒
    pthread_cond_timedwait(&cond,&mux_wait_lock,&tv);
    pthread_mutex_unlock(&mux_wait_lock);
}
// 线程睡眠 并让出cpu 秒
void http_time_sleep(long ssec)
{
    _http_time_sleep(ssec * 1000000000);
}

// 线程睡眠 并让出cpu 毫秒
void http_time_sleepm(long msec)
{
    _http_time_sleep(msec * 1000000);
}
// 线程睡眠 并让出cpu 纳秒
void http_time_sleepn(long nsec)
{
    _http_time_sleep(nsec);
}